#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string.h>
#include "Message.h"
#include "User.h"
#include "ReadWrite.h"
#include "MsgQueue.h"
#include "Message_type2.h"
#define IMAGE_SIZE (640*480/8+1)

using namespace std;

struct RawMessage {
	RawMessage() {
		dataStored = 0;
		complete = false;
		memset(data, 0, sizeof data);
		size = 256;
	}

	int read(char *buffer, int offset, int bytesLeftInBuffer) {
		int i = 0;
		/*
		 * Mamy wiecej lub rowno danych
		 */
		if (bytesLeftInBuffer >= (size - dataStored)) {
			for (; i < (size - dataStored); i++) {
				data[i + dataStored] = buffer[i + offset];
			}
			complete = true;
		}
		/*
		 * Mamy mniej danych niz potrzeba
		 */
		else {
			for (; i < bytesLeftInBuffer; ++i)
				data[i + dataStored] = buffer[i + offset];
		}

		dataStored += i;
		return i;
	}

	int dataStored;
	bool complete;
	char data[IMAGE_SIZE];
	int size;

};

void* readData(void * userSocket_) {
	char buf[256];
	int id;

	UserSocket* userSocket;
	userSocket = (UserSocket*) userSocket_;
	User * adrUser = userSocket->user_;
	Package *paczka;
	ChatPackage czat;
	ImagePackage obrazek;
	NumberPackage numerek;
	Package zwykla;

	Message wiadomosc;

	id = userSocket->sock;

	cerr << "user: " << adrUser->getId() << " lis th: " << pthread_self()
			<< endl;
	pthread_t thread;
	//Stworzenie wątku wysyłania danych
	pthread_create(&thread, NULL, writeData, (void*) userSocket);



	int numberOfBytesReadFormBuffer = 0;
	RawMessage *incompleteMessage = NULL;
	char buffer[256];

	while (true) {
		/*
		 * Wczytanie danych z bufora
		 */
		int dataReceiveLenght = read(id, buffer, sizeof(buffer));
		numberOfBytesReadFormBuffer = 0;
		/*
		 * Nic nie wczytalismy to znaczy ze stracilismy polaczenie
		 */
		if (dataReceiveLenght <= 0) {

			break;
		}

		/*
		 * Odczytujemy az skoncza nam sie bajty
		 */
		while (numberOfBytesReadFormBuffer < dataReceiveLenght) {
			RawMessage * message = NULL;
			/*
			 * Ostatnio odczytalismy wszystko wiec teraz mamy nowa wiadomosc w buforze
			 */
			if (incompleteMessage == NULL) {
				char messageCode = buffer[numberOfBytesReadFormBuffer];

				/*
				 * Tu mamy obrazek
				 */
				if (messageCode == 10) {
					message = new RawMessage();
					message->size = IMAGE_SIZE;
				} else {
					message = new RawMessage();
				}
			} else {
				message = incompleteMessage;
			}

			/*
			 * Wiadomosc odczytuje swoje dane z bufora
			 */
			int dataLeftInBuffer = dataReceiveLenght
					- numberOfBytesReadFormBuffer;

			int messageBytesRead = message->read(buffer,
					numberOfBytesReadFormBuffer, dataLeftInBuffer);

			if (message->complete) {
				/*
				 * Wiadomosc odczytala wszystkie swoje dane
				 */
				numberOfBytesReadFormBuffer += messageBytesRead;
				/*
				 * Wkladam ja do kolejki
				 */

				//Sprawdzenie kodu wiadomości i stworzenie odpowiedzniej wiadomości (Message)
				switch (message->data[0]) {

				//klient podaje login
				case (C_LOGIN):
					paczka = &czat;
					paczka->unpack(message->data, message->size);
					wiadomosc = Message(userSocket->user_, (ChatPackage) czat);

					break;

				//klient wylogowuje się
				case (C_LOGOUT):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient prosi o podanie listy pokoi
				case (C_ROOM_LIST):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient prosi o stworzenie nowego pokoju
				case (C_CREATE_ROOM):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient prosi o podłaczenie do pokoju
				case (C_JOIN_ROOM):
					paczka = &numerek;
					paczka->unpack((unsigned int) message->data[0],
							(unsigned int) message->data[1]);
					wiadomosc = Message(userSocket->user_, numerek);

					break;

				//klient prosi by go wypuścić z pokoju
				case (C_LEAVE_ROOM):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient prosi o rozpoczęcie nowej gry
				case (C_START_NEW_GAME):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//gracz rysujący nie chce już rysować
				case (C_GIVE_UP):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient oczekuje potwierdzenia połączenia
				case (CHECK_CONNECTION):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//potwierdzenie połączenia - odpowiedz na #29
				case (CONNECTION_OK):
					paczka = &zwykla;
					paczka->unpack((unsigned int) message->data[0]);
					wiadomosc = Message(userSocket->user_, zwykla);

					break;

				//klient wysyła wiadomość tekstową
				case (CHAT_MESSAGE):
					paczka = &czat;

					paczka->unpack(message->data, message->size);
					wiadomosc = Message(userSocket->user_, (ChatPackage) czat);

					break;

				//Klient wysyła obrazek
				case (IMAGE_MESSAGE):

					paczka = &obrazek;
					paczka->unpack(message->data, message->size);

					wiadomosc = Message(userSocket->user_, obrazek);


					break;

				default:
					cout << (unsigned int) message->data[0]
							<< " zly kod wiadomosci " << id << " " << buf
							<< "\n";
					break;
				}
				MsgQueue::getInstance().pushMessage(wiadomosc);
				/*
				 * Jesli to bylo dokonczenie sciagania
				 */
				if (incompleteMessage != NULL)
					incompleteMessage = NULL;

				delete message;
			} else {
				/*
				 * Wiadomosc nie odczytala wszystkich swoich danych
				 */
				incompleteMessage = message;
				numberOfBytesReadFormBuffer += messageBytesRead;
			}
		}
	}

	userSocket->user_->sendToClient(99);
	pthread_join(thread, NULL);

	return NULL;
}

void* writeData(void * userSocket_) {
	int id;

	UserSocket* userSocket;
	userSocket = (UserSocket*) userSocket_;
	Package *paczka;
	Package packageFromB;
	ChatPackage czat;
	ImagePackage obrazek;
	NumberPackage numerek;
	Package zwykla;

	Message wiadomosc;

	id = userSocket->sock;

	while (1) {

		paczka = userSocket->user_->messageFromBuffer();

		if (paczka != NULL) {
			paczka->pack();
			//Jeżeli kod wiadomości jest różny niż 10 (obrazek) wysłanie z użyciem standardowego bufora z obiektu Package
			if (paczka->code != 10) {
				if (paczka->code == 99) {
					wiadomosc = Message(userSocket->user_, Package(3));
					MsgQueue::getInstance().pushMessage(wiadomosc);
					break;
				} else {

					if (userSocket->user_ != NULL)
						write(id, paczka->sendBuffor,
								sizeof(paczka->sendBuffor));
				}
				//Wysłanie danych używajac bufora obrazka
			} else {
				write(id, static_cast<ImagePackage*> (paczka)->imageBuffer,
						IMAGE_SIZE);
			}

		}

	}

	close(id);
}
